using System;
using System.Collections.Generic;
using System.Linq;
using Urs.Core;
using Urs.Core.Caching;
using Urs.Core.Data;
using Urs.Data.Domain.Stores;
using Urs.Data.Domain.Users;
using Urs.Data.Domain.Orders;
using Urs.Data.Domain.Configuration;
using Urs.Services.Stores;
using Urs.Services.Users;
using Urs.Services.Localization;
using Urs.Services.Security;

namespace Urs.Services.Orders
{
    public partial class ShoppingCartService : IShoppingCartService
    {
        #region Fields

        private readonly IRepository<ShoppingCartItem> _sciRepository;
        private readonly IWorkContext _workContext;
        private readonly ICacheManager _cacheManager;
        private readonly IGoodsService _goodsService;
        private readonly ILocalizationService _stringResourceService;
        private readonly IGoodsSpecService _goodsSpecService;
        private readonly IGoodsSpecParser _goodsSpecParser;
        private readonly IUserService _userService;
        private readonly ShoppingCartSettings _shoppingCartSettings;
        private readonly IPermissionService _permissionService;
        #endregion

        #region Ctor

        public ShoppingCartService(IRepository<ShoppingCartItem> sciRepository,
            IWorkContext workContext, ICacheManager cacheManager,
            IGoodsService goodsService, ILocalizationService stringResourceService,
            IGoodsSpecService goodsSpecService,
            IGoodsSpecParser goodsSpecParser,
            IUserService userService,
            ShoppingCartSettings shoppingCartSettings,
            IPermissionService permissionService)
        {
            this._sciRepository = sciRepository;
            this._workContext = workContext;
            this._cacheManager = cacheManager;
            this._goodsService = goodsService;
            this._stringResourceService = stringResourceService;
            this._goodsSpecService = goodsSpecService;
            this._goodsSpecParser = goodsSpecParser;
            this._userService = userService;
            this._shoppingCartSettings = shoppingCartSettings;
            this._permissionService = permissionService;
        }

        #endregion

        #region Methods

        public virtual void DeleteShoppingCartItem(ShoppingCartItem shoppingCartItem,
            bool ensureOnlyActiveCheckoutAttributes = false)
        {
            if (shoppingCartItem == null)
                throw new ArgumentNullException("shoppingCartItem");

            var user = shoppingCartItem.User;

            _sciRepository.Delete(shoppingCartItem);

            user.HasShoppingCartItems = user.ShoppingCartItems.Count > 0;
            _userService.UpdateUser(user);

        }

        public virtual int DeleteExpiredShoppingCartItems(DateTime olderThanUtc)
        {
            var query = from sci in _sciRepository.Table
                        where sci.UpdateTime < olderThanUtc
                        select sci;

            var cartItems = query.ToList();
            foreach (var cartItem in cartItems)
                _sciRepository.Delete(cartItem);
            return cartItems.Count;
        }

        public virtual IList<string> GetStandardWarnings(User user, Goods goods, string selectedAttributes, int quantity)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            if (goods == null)
                throw new ArgumentNullException("goods");

            var warnings = new List<string>();

            if (goods == null)
            {
                warnings.Add(string.Format(_stringResourceService.GetResource("ShoppingCart.CannotLoadGoods"), goods.Id));
                return warnings;
            }

            if (goods.Deleted)
            {
                warnings.Add(_stringResourceService.GetResource("ShoppingCart.GoodsDeleted"));
                return warnings;
            }

            if (!goods.Published)
            {
                warnings.Add(_stringResourceService.GetResource("ShoppingCart.GoodsUnpublished"));
            }

            if (goods.ManageStockEnabled)
            {
                if (goods.StockQuantity < quantity)
                {
                    int maximumQuantityCanBeAdded = goods.StockQuantity;
                    if (maximumQuantityCanBeAdded <= 0)
                        warnings.Add(_stringResourceService.GetResource("ShoppingCart.OutOfStock"));
                    else
                        warnings.Add(string.Format(_stringResourceService.GetResource("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded));
                }
            }
            return warnings;
        }

        public virtual IList<string> GetShoppingCartItemAttributeWarnings(Goods goods, string selectedAttributes)
        {
            if (goods == null)
                throw new ArgumentNullException("goods");

            var warnings = new List<string>();

            var pva1Collection = _goodsSpecParser.ParseGoodsSpecs(selectedAttributes);
            var paMappings = _goodsSpecService.GetGoodsSpecMappingByGoodsId(goods.Id);
            foreach (var paMapping in paMappings)
            {
                foreach (var pva1 in pva1Collection)
                {
                    if (pva1.Id == paMapping.Id)
                    {
                        bool found = false;
                        var pvaValuesStr = _goodsSpecParser.ParseValues(selectedAttributes, pva1.Id);
                        foreach (string str1 in pvaValuesStr)
                        {
                            if (!String.IsNullOrEmpty(str1.Trim()))
                            {
                                found = true;
                                break;
                            }
                        }

                        if (!found)
                        {
                            warnings.Add(string.Format(_stringResourceService.GetResource("ShoppingCart.SelectAttribute"), paMapping.GoodsSpecName));
                        }
                    }
                }

            }

            return warnings;
        }

        public virtual IList<string> GetShoppingCartItemWarnings(User user,
            Goods goods, string selectedAttributes,
            int quantity, bool automaticallyAddRequiredGoodssIfEnabled,
            bool getStandardWarnings = true, bool getAttributesWarnings = true,
            bool getRequiredGoodsWarnings = true)
        {
            if (goods == null)
                throw new ArgumentNullException("goods");

            var warnings = new List<string>();
            if (getStandardWarnings)
                warnings.AddRange(GetStandardWarnings(user, goods, selectedAttributes, quantity));

            if (getAttributesWarnings)
                warnings.AddRange(GetShoppingCartItemAttributeWarnings( goods, selectedAttributes));

            return warnings;
        }

        public virtual IList<string> GetShoppingCartWarnings(IList<ShoppingCartItem> shoppingCart)
        {
            var warnings = new List<string>();

            foreach (var sci in shoppingCart)
            {
                if (sci.Goods == null)
                {
                    warnings.Add(string.Format(_stringResourceService.GetResource("ShoppingCart.CannotLoadGoodsVariant"), sci.Goods.Id));
                    return warnings;
                }
            }

            return warnings;
        }

        public virtual ShoppingCartItem FindShoppingCartItemInTheCart(IList<ShoppingCartItem> shoppingCart,
            Goods goods,
            string selectedAttributes = "")
        {
            if (shoppingCart == null)
                throw new ArgumentNullException("shoppingCart");

            if (goods == null)
                throw new ArgumentNullException("goods");

            foreach (var sci in shoppingCart)
            {
                if (sci.Goods.Id == goods.Id)
                {
                    bool attributesEqual = _goodsSpecParser.AreGoodsSpecsEqual(sci.AttributesXml, selectedAttributes);

                    if (attributesEqual)
                        return sci;
                }
            }

            return null;
        }

        public virtual IList<string> AddToCart(User user, Goods goods, string selectedAttributes,
            decimal userEnteredPrice, int quantity, bool automaticallyAddRequiredGoodssIfEnabled,
            out int cartItemId,
            bool selected = false, bool tocheckout = false, int ptUserId = 0)
        {
            cartItemId = 0;
            if (user == null)
                throw new ArgumentNullException("user");

            if (goods == null)
                throw new ArgumentNullException("goods");

            var warnings = new List<string>();

            if (!string.IsNullOrEmpty(selectedAttributes))
            {
                var combination = _goodsSpecParser.FindGoodsSpecCombination(goods, selectedAttributes);
                if (combination == null)
                    warnings.Add(_stringResourceService.GetResource("ShoppingCart.NotExsitAttributeCombination"));
            }

            var cart = user.ShoppingCartItems.ToList();
            var shoppingCartItem = FindShoppingCartItemInTheCart(cart, goods, selectedAttributes);

            if (shoppingCartItem != null)
            {
                int newQuantity = selected ? quantity : shoppingCartItem.Quantity + quantity;

                warnings.AddRange(GetShoppingCartItemWarnings(user, goods,
                    selectedAttributes, newQuantity, automaticallyAddRequiredGoodssIfEnabled));

                if (warnings.Count == 0)
                {
                    if (tocheckout)
                        foreach (var item in user.ShoppingCartItems)
                            item.Selected = false;

                    shoppingCartItem.AttributesXml = selectedAttributes;
                    shoppingCartItem.Quantity = newQuantity;
                    shoppingCartItem.GoodsId = goods.Id;
                    shoppingCartItem.Selected = selected;
                    shoppingCartItem.UpdateTime = DateTime.Now;
                    _userService.UpdateUser(user);
                    cartItemId = shoppingCartItem.Id;
                }
            }
            else
            {
                warnings.AddRange(GetShoppingCartItemWarnings(user,  goods,
                    selectedAttributes, quantity, automaticallyAddRequiredGoodssIfEnabled));
                if (warnings.Count == 0)
                {
                    if (cart.Count >= _shoppingCartSettings.MaximumShoppingCartItems)
                        return warnings;

                    DateTime now = DateTime.Now;
                    shoppingCartItem = new ShoppingCartItem()
                    {
                        User = user,
                        AttributesXml = selectedAttributes,
                        GoodsId = goods.Id,
                        Quantity = quantity,
                        CreateTime = now,
                        UpdateTime = now,
                        Goods = goods,
                        Selected = selected
                    };
                    if (tocheckout)
                        foreach (var item in user.ShoppingCartItems)
                            item.Selected = false;
                    user.ShoppingCartItems.Add(shoppingCartItem);
                    _userService.UpdateUser(user);
                    cartItemId = shoppingCartItem.Id;
                    user.HasShoppingCartItems = user.ShoppingCartItems.Count > 0;
                    _userService.UpdateUser(user);

                }
            }

            return warnings;
        }

        public virtual IList<string> UpdateShoppingCartItem(User user, int shoppingCartItemId,
            int newQuantity, bool? selected = null)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            var warnings = new List<string>();

            var shoppingCartItem = user.ShoppingCartItems.Where(sci => sci.Id == shoppingCartItemId).FirstOrDefault();

            if (shoppingCartItem != null)
            {
                if (newQuantity > 0)
                {
                    warnings.AddRange(GetShoppingCartItemWarnings(user, shoppingCartItem.Goods, shoppingCartItem.AttributesXml, newQuantity, false));
                    if (warnings.Count == 0)
                    {
                        shoppingCartItem.Quantity = newQuantity;
                        shoppingCartItem.UpdateTime = DateTime.Now;
                        if (selected.HasValue)
                            shoppingCartItem.Selected = selected.Value;

                        _userService.UpdateUser(user);
                    }
                }
                else
                {
                    DeleteShoppingCartItem(shoppingCartItem, true);
                }
            }
            return warnings;
        }

        public virtual void UpdateShoppingCartItemSelected(User user, IList<ShoppingCartItem> shoppingCart, bool selected)
        {
            if (shoppingCart == null)
                throw new ArgumentNullException("user");

            if (shoppingCart != null)
            {
                var scIds = shoppingCart.Select(x => x.Id);

                var shoppingCartItems = user.ShoppingCartItems.Where(sci => scIds.Contains(sci.Id));

                foreach (var shoppingCartItem in shoppingCartItems)
                {
                    shoppingCartItem.Selected = selected;
                    shoppingCartItem.UpdateTime = DateTime.Now;
                }

                _userService.UpdateUser(user);
            }
        }

        public virtual void MigrateShoppingCart(User fromUser, User toUser)
        {
            if (fromUser == null)
                throw new ArgumentNullException("fromUser");
            if (toUser == null)
                throw new ArgumentNullException("toUser");

            if (fromUser.Id == toUser.Id)
                return; //the same user

            var fromCart = fromUser.ShoppingCartItems.ToList();
            for (int i = 0; i < fromCart.Count; i++)
            {
                var sci = fromCart[i];

                AddToCart(toUser, sci.Goods,sci.AttributesXml, decimal.Zero, sci.Quantity, false, out int cartItemId);
            }
            for (int i = 0; i < fromCart.Count; i++)
            {
                var sci = fromCart[i];
                DeleteShoppingCartItem(sci);
            }
        }

        #endregion


    }
}
